package com.example.webgobang.component;

import com.example.webgobang.mapper.ChessHistoryMapper;
import com.example.webgobang.mapper.UserMapper;
import com.example.webgobang.model.domin.ChessHistory;
import com.example.webgobang.model.domin.User;
import com.example.webgobang.model.game.GameReadyResponse;
import com.example.webgobang.model.game.GameRequest;
import com.example.webgobang.model.game.GameResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.annotation.Resource;
import java.io.IOException;

import static com.example.webgobang.common.Constants.SESSION_USER_KEY;

@Slf4j
@Component
public class GameHandler extends TextWebSocketHandler {
    @Resource
    private RoomManager roomManager;

    @Resource
    private OnlineUserManager onlineUserManager;

    @Resource
    private UserMapper userMapper;

    @Resource
    private ChessHistoryMapper chessHistoryMapper;

    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        GameReadyResponse response = new GameReadyResponse();
        // 检查登录态
        User user = (User) session.getAttributes().get(SESSION_USER_KEY);
        if(user == null) {
            response.setOk(false);
            response.setReason("用户未登录");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
            return;
        }
        // 检查可曾进入房间
        Room room = roomManager.getRoomByUserId(user.getId());
        if(room == null) {
            response.setOk(false);
            response.setReason("用户未经匹配");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
            return;
        }
        // 检查多开，离开大厅后session已从大厅用户表删除，进入房间后session尚未录入房间用户表，此时session不应在任一表中
        if(onlineUserManager.getSessionFromGameHall(user.getId()) != null
                || onlineUserManager.getSessionFromGameRoom(user.getId()) != null) {
            response.setOk(false);
            response.setReason("用户多开");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
            return;
        }
        // 玩家上线
        synchronized (room) {
            onlineUserManager.enterGameRoom(user.getId(), session);
            if (room.getUser1() == null) {
                room.setUser1(user);
                room.setBlackUserId(user.getId());
                log.info(user.getUsername() + " is ready, gets black");
                return;
            }
            if (room.getUser2() == null) {
                room.setUser2(user);
                log.info(user.getUsername() + " is ready, gets white");
                noticeGameReady(room, room.getUser1(), room.getUser2());
                noticeGameReady(room, room.getUser2(), room.getUser1());
                return;
            }
        }
        response.setOk(false);
        response.setReason("房间已满，无法加入");
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
    }

    private void noticeGameReady(Room room, User thisUser, User thatUser) throws IOException {
        GameReadyResponse response = new GameReadyResponse();
        response.setMessage("gameReady");
        response.setOk(true);
        response.setRoomId(room.getRoomId());
        response.setThisUser(thisUser);
        response.setThatUser(thatUser);
        response.setBlackUserId(room.getBlackUserId());
        WebSocketSession session = onlineUserManager.getSessionFromGameRoom(thisUser.getId());
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        User user = (User) session.getAttributes().get(SESSION_USER_KEY);
        if(user == null) {
            log.error("[GameWebSocketCallBacks-handleTextMessage] 玩家未登录");
            return;
        }
        Room room = roomManager.getRoomByUserId(user.getId());
        GameRequest request = objectMapper.readValue(message.getPayload(), GameRequest.class);
        if("putChess".equals(request.getMessage())) {
            room.putChess(request);
        } else if("surrender".equals(request.getMessage())) {
            room.surrender(request);
        } else {
            log.error("[GameWebSocketCallBacks-handleTextMessage] 未知请求");
        }
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        User user = (User) session.getAttributes().get(SESSION_USER_KEY);
        if(user == null) {
            return;
        }
        WebSocketSession exitSession = onlineUserManager.getSessionFromGameRoom(user.getId());
        if(exitSession == session) {
            onlineUserManager.exitGameRoom(user.getId());
        }
        noticeThatUserWin(user);
        log.info(user.getUsername() + " 连接异常");
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        User user = (User) session.getAttributes().get(SESSION_USER_KEY);
        if(user == null) {
            return;
        }
        WebSocketSession exitSession = onlineUserManager.getSessionFromGameRoom(user.getId());
        if(exitSession == session) {
            onlineUserManager.exitGameRoom(user.getId());
        }
        noticeThatUserWin(user);
        log.info(user.getUsername() + " 离开房间");
    }

    private void noticeThatUserWin(User thisUser) throws IOException {
        Room room = roomManager.getRoomByUserId(thisUser.getId());
        if(room == null) {
            return;
        }
        User thatUser = (thisUser == room.getUser1()) ? room.getUser2() : room.getUser1();
        WebSocketSession session = onlineUserManager.getSessionFromGameRoom(thatUser.getId());
        if(session == null) {
            return;
        }
        // 记录棋谱
        if(chessHistoryMapper.selectHistoryByRoomId(room.getRoomId()) == null) {
            String historyJson = objectMapper.writeValueAsString(room.getChessPointList());
            ChessHistory chessHistory = makeChessHistory(room, historyJson, thatUser);
            chessHistoryMapper.insertHistory(chessHistory);
        }
        // 通知对手获胜
        GameResponse response = new GameResponse();
        response.setMessage("disconnected");
        response.setWinnerId(thatUser.getId());
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
        // 更新数据库分数信息
        userMapper.updateWinUser(thatUser.getId());
        userMapper.updateLoseUser(thisUser.getId());
        // 释放房间对象
        roomManager.remove(room.getRoomId(), room.getUser1().getId(), room.getUser2().getId());
    }

    private ChessHistory makeChessHistory(Room room, String historyJson, User thatUser) {
        ChessHistory chessHistory = new ChessHistory();
        chessHistory.setRoomId(room.getRoomId());
        chessHistory.setHistory(historyJson);
        chessHistory.setUserId1(room.getUser1().getId());
        chessHistory.setUsername1(room.getUser1().getUsername());
        chessHistory.setUserId2(room.getUser2().getId());
        chessHistory.setUsername2(room.getUser2().getUsername());
        chessHistory.setBlackUserId(room.getBlackUserId());
        chessHistory.setWinnerId(thatUser.getId());
        return chessHistory;
    }
}
